// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package ssa

import (
	"cmd/compile/internal/abi"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"fmt"
	"strings"
)

// Op对值执行的特定操作进行编码。
// 操作码的语义可以通过值的type和aux字段进行修改。
// 例如，OpAdd可以是32位或64位、有符号或无符号、浮点或复数，具体取决于Value.Type。
// gen/*Ops.go中的操作码文件描述了每个操作的语义。
// 通用（独立于体系结构）ops有一个文件和一个文件
// 对于每个架构。
type Op int32

type opInfo struct {
	name              string
	reg               regInfo
	auxType           auxType
	argLen            int32 // 参数数，如果长度可变，则为-1
	asm               obj.As
	generic           bool      // 这是一个通用（与arch无关）操作码
	rematerializeable bool      // 此操作是可重物质化的
	commutative       bool      // 此操作是可交换的（例如加法）
	resultInArg0      bool      // （首先，如果是元组）v和v.Args[0]的输出必须分配到同一寄存器
	resultNotInArgs   bool      // 输出不得分配给与输入相同的寄存器
	clobberFlags      bool      // 此操作将关闭标志寄存器
	call              bool      // 是一个函数调用
	nilCheck          bool      // 此op是arg0上的零检查
	faultOnNilArg0    bool      // 如果arg0为nil（且aux编码小偏移量），则此op将出现故障
	faultOnNilArg1    bool      // 如果arg1为nil（且aux编码小偏移量），则此op将出现故障
	usesScratch       bool      // 此操作需要临时内存空间
	hasSideEffects    bool      // 由于“原因”，无法消除。例如，原子商店#19182。
	zeroWidth         bool      // op从不翻译成任何机器代码。例如：有时可能转换为机器代码的副本不是零宽度。
	unsafePoint       bool      // 此op是一个不安全点，即异步抢占不安全
	symEffect         SymEffect // 此op对aux中符号的影响
	scale             uint8     // amd64/386索引负载比例
}

type inputInfo struct {
	idx  int     // Args数组中的索引
	regs regMask // 允许输入寄存器
}

type outputInfo struct {
	idx  int     // 输出元组中的索引
	regs regMask // 允许的输出寄存器
}

type regInfo struct {
	// 输入对指令输入的寄存器限制进行编码。
	// 每个条目为特定输入指定一个允许的寄存器集。
	// 它们按regalloc选择寄存器的顺序列出
	// 从寄存器集（最受约束的第一个）。
	// 不需要寄存器的输入未列出。
	inputs []inputInfo
	// clobbers对被覆盖的寄存器集进行编码
	// 指令（输出寄存器除外）。
	clobbers regMask
	// 输出与输入相同，但用于指令的输出。
	outputs []outputInfo
}

func (r *regInfo) String() string {
	s := ""
	s += "INS:\n"
	for _, i := range r.inputs {
		mask := fmt.Sprintf("%64b", i.regs)
		mask = strings.Replace(mask, "0", ".", -1)
		s += fmt.Sprintf("%2d |%s|\n", i.idx, mask)
	}
	s += "OUTS:\n"
	for _, i := range r.outputs {
		mask := fmt.Sprintf("%64b", i.regs)
		mask = strings.Replace(mask, "0", ".", -1)
		s += fmt.Sprintf("%2d |%s|\n", i.idx, mask)
	}
	s += "CLOBBERS:\n"
	mask := fmt.Sprintf("%64b", r.clobbers)
	mask = strings.Replace(mask, "0", ".", -1)
	s += fmt.Sprintf("   |%s|\n", mask)
	return s
}

type auxType int8

type AuxNameOffset struct {
	Name   *ir.Name
	Offset int64
}

func (a *AuxNameOffset) CanBeAnSSAAux() {}
func (a *AuxNameOffset) String() string {
	return fmt.Sprintf("%s+%d", a.Name.Sym().Name, a.Offset)
}

type AuxCall struct {
	Fn      *obj.LSym
	reg     *regInfo // 此呼叫的regInfo
	abiInfo *abi.ABIParamResultInfo
}

// Reg返回给定调用的regInfo，并结合派生的输入/输出寄存器掩码
// 输入i中的机器特定寄存器信息。（机器特定）
// regInfo在调用站点上比构建AuxCall时要方便得多，
// 因此，请懒散地这样做）。
// None
// TODO：有一个聪明的黑客程序，它允许预生成少量的切片
// 这里使用的inputInfo和OutputinInfo，前提是我们愿意对输入进行重新排序
// 和调用的输出，以便所有整数寄存器都排在第一位，然后是所有浮点寄存器。
// 此时（寄存器ABI的积极开发）还为时过早，
// 但如果这是一个成本，我们可以这样做。
func (a *AuxCall) Reg(i *regInfo, c *Config) *regInfo {
	if a.reg.clobbers != 0 {
		// 已经更新
		return a.reg
	}
	if a.abiInfo.InRegistersUsed()+a.abiInfo.OutRegistersUsed() == 0 {
		// 零大小写的快捷方式，也处理旧ABI。
		a.reg = i
		return a.reg
	}

	k := len(i.inputs)
	for _, p := range a.abiInfo.InParams() {
		for _, r := range p.Registers {
			m := archRegForAbiReg(r, c)
			a.reg.inputs = append(a.reg.inputs, inputInfo{idx: k, regs: (1 << m)})
			k++
		}
	}
	a.reg.inputs = append(a.reg.inputs, i.inputs...) // 这些限制较少，因此应该排在最后
	k = len(i.outputs)
	for _, p := range a.abiInfo.OutParams() {
		for _, r := range p.Registers {
			m := archRegForAbiReg(r, c)
			a.reg.outputs = append(a.reg.outputs, outputInfo{idx: k, regs: (1 << m)})
			k++
		}
	}
	a.reg.outputs = append(a.reg.outputs, i.outputs...)
	a.reg.clobbers = i.clobbers
	return a.reg
}
func (a *AuxCall) ABI() *abi.ABIConfig {
	return a.abiInfo.Config()
}
func (a *AuxCall) ABIInfo() *abi.ABIParamResultInfo {
	return a.abiInfo
}
func (a *AuxCall) ResultReg(c *Config) *regInfo {
	if a.abiInfo.OutRegistersUsed() == 0 {
		return a.reg
	}
	if len(a.reg.inputs) > 0 {
		return a.reg
	}
	k := 0
	for _, p := range a.abiInfo.OutParams() {
		for _, r := range p.Registers {
			m := archRegForAbiReg(r, c)
			a.reg.inputs = append(a.reg.inputs, inputInfo{idx: k, regs: (1 << m)})
			k++
		}
	}
	return a.reg
}

// 对于ABI寄存器索引r，返回在
// SSA后端。
func archRegForAbiReg(r abi.RegIndex, c *Config) uint8 {
	var m int8
	if int(r) < len(c.intParamRegs) {
		m = c.intParamRegs[r]
	} else {
		m = c.floatParamRegs[int(r)-len(c.intParamRegs)]
	}
	return uint8(m)
}

// 对于ABI寄存器索引r，返回obj中使用的寄存器编号
// 软件包（汇编程序）。
func ObjRegForAbiReg(r abi.RegIndex, c *Config) int16 {
	m := archRegForAbiReg(r, c)
	return c.registers[m].objNum
}

// ArgWidth返回所有输入所需的堆栈量
// 函数或方法的输出，包括ABI定义的参数
// 插槽和ABI为寄存器驻留参数定义的溢出插槽。
// None
// 名称取自类型包的ArgWidth（<function type>），
// 在ABI发生变化之前；此版本处理这些更改。
func (a *AuxCall) ArgWidth() int64 {
	return a.abiInfo.ArgWidth()
}

// ParamAssignmentForResult为索引为0、1等的结果返回ABI参数赋值。
func (a *AuxCall) ParamAssignmentForResult(which int64) *abi.ABIParamAssignment {
	return a.abiInfo.OutParam(int(which))
}

// OffsetOfResult返回索引为0、1等的结果的SP偏移量。
func (a *AuxCall) OffsetOfResult(which int64) int64 {
	n := int64(a.abiInfo.OutParam(int(which)).Offset())
	return n
}

// OffsetOfArg返回参数的SP偏移量（索引为0、1等）。
// 如果调用的是方法，则接收方是第一个参数（即索引0）
func (a *AuxCall) OffsetOfArg(which int64) int64 {
	n := int64(a.abiInfo.InParam(int(which)).Offset())
	return n
}

// RegsOfResult返回用于结果的寄存器（索引为0、1等）。
func (a *AuxCall) RegsOfResult(which int64) []abi.RegIndex {
	return a.abiInfo.OutParam(int(which)).Registers
}

// RegsOfArg返回用于参数（索引为0、1等）的寄存器。
// 如果调用的是方法，则接收方是第一个参数（即索引0）
func (a *AuxCall) RegsOfArg(which int64) []abi.RegIndex {
	return a.abiInfo.InParam(int(which)).Registers
}

// NameOfResult返回索引为0、1等的结果类型。
func (a *AuxCall) NameOfResult(which int64) *ir.Name {
	name := a.abiInfo.OutParam(int(which)).Name
	if name == nil {
		return nil
	}
	return name.(*ir.Name)
}

// TypeOfResult返回索引为0、1等的结果类型。
func (a *AuxCall) TypeOfResult(which int64) *types.Type {
	return a.abiInfo.OutParam(int(which)).Type
}

// TypeOfArg返回参数的类型（索引为0、1等）。
// 如果调用的是方法，则接收方是第一个参数（即索引0）
func (a *AuxCall) TypeOfArg(which int64) *types.Type {
	return a.abiInfo.InParam(int(which)).Type
}

// SizeOfResult返回索引为0、1等的结果的大小。
func (a *AuxCall) SizeOfResult(which int64) int64 {
	return a.TypeOfResult(which).Width
}

// SizeOfArg返回参数的大小（索引为0、1等）。
// 如果调用的是方法，则接收方是第一个参数（即索引0）
func (a *AuxCall) SizeOfArg(which int64) int64 {
	return a.TypeOfArg(which).Width
}

// NResults返回结果数
func (a *AuxCall) NResults() int64 {
	return int64(len(a.abiInfo.OutParams()))
}

// LateExpansionResultType返回结果类型（包括尾部mem）
// 对于稍后将在SSA阶段展开的呼叫。
func (a *AuxCall) LateExpansionResultType() *types.Type {
	var tys []*types.Type
	for i := int64(0); i < a.NResults(); i++ {
		tys = append(tys, a.TypeOfResult(i))
	}
	tys = append(tys, types.TypeMem)
	return types.NewResults(tys)
}

// NArgs返回参数的数量（包括receiver，如果有）。
func (a *AuxCall) NArgs() int64 {
	return int64(len(a.abiInfo.InParams()))
}

// 字符串返回“AuxCall{<fn>}”
func (a *AuxCall) String() string {
	var fn string
	if a.Fn == nil {
		fn = "AuxCall{nil" // 可能是接口/关闭等。
	} else {
		fn = fmt.Sprintf("AuxCall{%v", a.Fn)
	}
	// TODO应该打印多少ABI？

	return fn + "}"
}

// StaticAuxCall为静态调用返回一个AuxCall。
func StaticAuxCall(sym *obj.LSym, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
	if paramResultInfo == nil {
		panic(fmt.Errorf("Nil paramResultInfo, sym=%v", sym))
	}
	var reg *regInfo
	if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
		reg = &regInfo{}
	}
	return &AuxCall{Fn: sym, abiInfo: paramResultInfo, reg: reg}
}

// InterfaceAuxCall为接口调用返回AuxCall。
func InterfaceAuxCall(paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
	var reg *regInfo
	if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
		reg = &regInfo{}
	}
	return &AuxCall{Fn: nil, abiInfo: paramResultInfo, reg: reg}
}

// ClosureAuxCall为闭包调用返回一个AuxCall。
func ClosureAuxCall(paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
	var reg *regInfo
	if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
		reg = &regInfo{}
	}
	return &AuxCall{Fn: nil, abiInfo: paramResultInfo, reg: reg}
}

func (*AuxCall) CanBeAnSSAAux() {}

// OwnAuxCall返回函数自己的AuxCall
func OwnAuxCall(fn *obj.LSym, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
	// TODO如果在完成新的ABI后仍与上面的ClosureAuxCall相同，则应执行重复数据消除。
	var reg *regInfo
	if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
		reg = &regInfo{}
	}
	return &AuxCall{Fn: fn, abiInfo: paramResultInfo, reg: reg}
}

const (
	auxNone           auxType = iota
	auxBool                   // 生长素为0/1表示假/真
	auxInt8                   // 生长素是一个8位整数
	auxInt16                  // 生长素是一个16位整数
	auxInt32                  // 生长素是一个32位整数
	auxInt64                  // 生长素是一个64位整数
	auxInt128                 // auxInt表示128位整数。总是0。
	auxUInt8                  // auxInt是一个8位无符号整数
	auxFloat32                // auxInt是一个float32（用math.float64位编码）
	auxFloat64                // auxInt是一个float64（用math.float64位编码）
	auxFlagConstant           // 生长素是一个常数
	auxNameOffsetInt8         // aux是一个&struct{Name ir.Name，Offset int64}；生长素是参数寄存器数组中的索引
	auxString                 // aux是一个字符串
	auxSym                    // aux是一个符号（局部为*gc.Node，全局为*obj.LSym，无为nil）
	auxSymOff                 // aux是一个符号，auxInt是一个偏移量
	auxSymValAndOff           // aux是一个符号，auxInt是ValAndOff
	auxTyp                    // aux是一种
	auxTypSize                // aux是一个类型，auxInt是一个大小，必须有aux.（类型）。size（）==auxInt
	auxCCop                   // aux是一个ssa.Op，表示从标志到布尔的转换（例如LessThan）
	auxCall                   // aux是一个*ssa.AuxCall
	auxCallOff                // aux是一个*ssa.AuxCall，AuxInt是int64参数（in+out）大小

	// 特定于体系结构的aux类型
	auxARM64BitField     // aux是一个arm64位字段lsb，宽度压缩到auxInt中
	auxS390XRotateParams // aux是s390x旋转参数对象编码起始位、结束位和旋转量
	auxS390XCCMask       // aux是一个s390x 4位条件代码掩码
	auxS390XCCMaskInt8   // aux是一个s390x 4位条件代码掩码，auxInt是一个int8立即数
	auxS390XCCMaskUint8  // aux是一个s390x 4位条件代码掩码，auxInt是一个uint8立即数
)

// 符号效应描述SSA值对变量的影响
// 由其Aux字段中的符号标识。
type SymEffect int8

const (
	SymRead SymEffect = 1 << iota
	SymWrite
	SymAddr

	SymRdWr = SymRead | SymWrite

	SymNone SymEffect = 0
)

// Sym表示基址寄存器的符号偏移量。
// 目前，Sym可以是以下三种情况之一：
// -a*gc.Node，表示与SP的偏移量（堆栈指针）
// -a*obj.LSym，表示与SB（全局指针）的偏移量
// -零，无偏移
type Sym interface {
	CanBeAnSSASym()
	CanBeAnSSAAux()
}

// ValAndOff由多个操作码使用。它坚持
// 值和指针偏移量。
// ValAndOff被编码到生长素字段中。
// 零ValAndOff对值0和偏移量0进行编码。
// 高32位保存一个值。
// 低32位保存指针偏移量。
type ValAndOff int64

func (x ValAndOff) Val() int32   { return int32(int64(x) >> 32) }
func (x ValAndOff) Val64() int64 { return int64(x) >> 32 }
func (x ValAndOff) Val16() int16 { return int16(int64(x) >> 32) }
func (x ValAndOff) Val8() int8   { return int8(int64(x) >> 32) }

func (x ValAndOff) Off64() int64 { return int64(int32(x)) }
func (x ValAndOff) Off() int32   { return int32(x) }

func (x ValAndOff) String() string {
	return fmt.Sprintf("val=%d,off=%d", x.Val(), x.Off())
}

// validVal报告是否可以使用该值
// 作为马克瓦兰多夫的论点。
func validVal(val int64) bool {
	return val == int64(int32(val))
}

func makeValAndOff(val, off int32) ValAndOff {
	return ValAndOff(int64(val)<<32 + int64(uint32(off)))
}

func (x ValAndOff) canAdd32(off int32) bool {
	newoff := x.Off64() + int64(off)
	return newoff == int64(int32(newoff))
}
func (x ValAndOff) canAdd64(off int64) bool {
	newoff := x.Off64() + off
	return newoff == int64(int32(newoff))
}

func (x ValAndOff) addOffset32(off int32) ValAndOff {
	if !x.canAdd32(off) {
		panic("invalid ValAndOff.addOffset32")
	}
	return makeValAndOff(x.Val(), x.Off()+off)
}
func (x ValAndOff) addOffset64(off int64) ValAndOff {
	if !x.canAdd64(off) {
		panic("invalid ValAndOff.addOffset64")
	}
	return makeValAndOff(x.Val(), x.Off()+int32(off))
}

// int128是一种存储128位常量的类型。
// 现在唯一允许的常量是0，所以我们可以作弊很多。
type int128 int64

type BoundsKind uint8

const (
	BoundsIndex       BoundsKind = iota // 索引操作，0<=idx<len失败
	BoundsIndexU                        // ... 使用未签名的idx
	BoundsSliceAlen                     // 2-arg切片操作，0<=高<=透镜失败
	BoundsSliceAlenU                    // ... 无符号高
	BoundsSliceAcap                     // 2-arg切片操作，0<=高<=cap失败
	BoundsSliceAcapU                    // ... 无符号高
	BoundsSliceB                        // 2-arg切片操作，0<=低<=高失败
	BoundsSliceBU                       // ... 无符号低
	BoundsSlice3Alen                    // 3-arg切片操作，0<=max<=len失败
	BoundsSlice3AlenU                   // ... 不带符号的max
	BoundsSlice3Acap                    // 3-arg切片操作，0<=最大值<=cap失败
	BoundsSlice3AcapU                   // ... 不带符号的max
	BoundsSlice3B                       // 3-arg切片操作，0<=高<=最大失败
	BoundsSlice3BU                      // ... 无符号高
	BoundsSlice3C                       // 3-arg切片操作，0<=低<=高失败
	BoundsSlice3CU                      // ... 无符号低
	BoundsConvert                       // 转换为数组指针失败
	BoundsKindCount
)

// boundsAPI确定边界检查调用应使用的寄存器参数。对于[a:b:c]切片，我们执行以下操作：
// CMPQ c，cap
// JA故障1
// CMPQ b，c
// JA故障2
// CMPQ a，b
// JA故障3
// None
// 故障1：呼叫cap（c，cap）
// 故障2：呼叫3B（b，c）
// 故障3：呼叫3C（a，b）
// None
// 当我们注册并分配代码时，我们希望将相同的寄存器用于
// panicSlice3Acap的第一个arg和panicSlice3B的第二个arg。这样,，
// 初始化该寄存器一次将满足两个调用。
// 这种愿望最终将边界检查调用集划分为3个集合。此函数
// 确定用于给定紧急呼叫的集合。
// 集合0的第一个参数应该是集合1的第二个参数。
// 集合1的第一个参数应该是集合2的第二个参数。
func boundsABI(b int64) int {
	switch BoundsKind(b) {
	case BoundsSlice3Alen,
		BoundsSlice3AlenU,
		BoundsSlice3Acap,
		BoundsSlice3AcapU,
		BoundsConvert:
		return 0
	case BoundsSliceAlen,
		BoundsSliceAlenU,
		BoundsSliceAcap,
		BoundsSliceAcapU,
		BoundsSlice3B,
		BoundsSlice3BU:
		return 1
	case BoundsIndex,
		BoundsIndexU,
		BoundsSliceB,
		BoundsSliceBU,
		BoundsSlice3C,
		BoundsSlice3CU:
		return 2
	default:
		panic("bad BoundsKind")
	}
}

// arm64BitFileld是ARM64BitField生长素的GO类型。
// 如果x是一个ARM64位字段，那么width=x&0xff，lsb=（x>>8）&0xff，以及
// 对于64位变量，宽度+lsb<64；对于32位变量，宽度+lsb<32。
// 宽度和lsb的含义取决于指令。
type arm64BitField int16
